package com.spzx.order.service.impl;

import com.alibaba.nacos.client.naming.utils.CollectionUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.cart.api.RemoteCartService;
import com.spzx.cart.domain.CartInfo;
import com.spzx.common.core.constant.SecurityConstants;
import com.spzx.common.core.context.SecurityContextHolder;
import com.spzx.common.core.domain.R;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.core.utils.StringUtils;
import com.spzx.common.core.utils.uuid.UUID;
import com.spzx.common.rabbit.constant.MqConst;
import com.spzx.common.rabbit.service.RabbitService;
import com.spzx.order.configure.DelayedMqConfig;
import com.spzx.order.domain.*;
import com.spzx.order.mapper.OrderInfoMapper;
import com.spzx.order.mapper.OrderItemMapper;
import com.spzx.order.mapper.OrderLogMapper;
import com.spzx.order.service.OrderInfoService;
import com.spzx.product.api.RemoteProductService;
import com.spzx.product.domain.ProductSku;
import com.spzx.product.domain.SkuLockVo;
import com.spzx.product.domain.SkuPrice;
import com.spzx.user.api.RemoteUserAddressService;
import com.spzx.user.domain.UserAddress;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author 作者
 * @create 2025-06-03-18:38
 */
@Service
public class OrderInfoServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo> implements OrderInfoService {

    @Autowired
    private RemoteCartService remoteCartService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RemoteProductService remoteProductService;

    @Autowired
    private RemoteUserAddressService remoteUserAddressService;

    @Autowired
    private OrderLogMapper orderLogMapper;

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderItemMapper  orderItemMapper;

    @Autowired
    private RabbitService rabbitService;

    @Override
    public TradeVo orderTradeData() {

        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        R<List<CartInfo>> cartInfoListResult = remoteCartService.getCartCheckedList(userId, SecurityConstants.INNER);
        if (R.FAIL == cartInfoListResult.getCode()) {
            throw new ServiceException(cartInfoListResult.getMsg());
        }
        List<CartInfo> cartInfoList = cartInfoListResult.getData();
        if (CollectionUtils.isEmpty(cartInfoList)) {
            throw new ServiceException("购物车无选中商品");
        }

        //将集合泛型从购物车改为订单明细
        List<OrderItem> orderItemList = null;
        BigDecimal totalAmount = new BigDecimal(0);
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            orderItemList = cartInfoList.stream().map(cartInfo -> {
                OrderItem orderItem = new OrderItem();
                BeanUtils.copyProperties(cartInfo, orderItem);
                orderItem.setSkuPrice(cartInfo.getSkuPrice());
                orderItem.setSkuNum(cartInfo.getSkuNum());
                return orderItem;
            }).collect(Collectors.toList());

            //订单总金额
            for(OrderItem orderItem : orderItemList) {
                totalAmount = totalAmount.add(orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum())));
            }
        }
        //渲染订单确认页面-生成用户流水号
        String tradeNo = this.generateTradeNo(userId);

        TradeVo tradeVo = new TradeVo();
        tradeVo.setTotalAmount(totalAmount);
        tradeVo.setOrderItemList(orderItemList);
        tradeVo.setTradeNo(tradeNo);

        return tradeVo;
    }

    @Override
    public TradeVo buy(Long skuId) {
        // 获取当前登录用户的id
        Long userId = SecurityContextHolder.getUserId();
        //获取去当前skuid的商品信息
        ProductSku productSku = remoteProductService.getProductSku(skuId, SecurityConstants.INNER).getData();
        if (productSku == null){
            throw new ServiceException("商品不存在");
        }

        //将集合泛型从购物车改为订单明细
        List<OrderItem> orderItemList = new ArrayList<>();;
        BigDecimal totalAmount = new BigDecimal(0);
        OrderItem orderItem = new OrderItem();
        orderItem.setSkuId(skuId);
        orderItem.setSkuPrice(productSku.getSalePrice()); // 根据实际情况设置价格
        orderItem.setSkuName(productSku.getSkuName());
        orderItem.setThumbImg(productSku.getThumbImg());
        orderItem.setSkuNum(1); // 设置数量，默认为1或其他需要的数量
        orderItemList.add(orderItem);

        //订单总金额
        for(OrderItem orderItem1 : orderItemList) {
            totalAmount = totalAmount.add(orderItem1.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum())));
        }

        //渲染订单确认页面-生成用户流水号
        String tradeNo = this.generateTradeNo(userId);

        TradeVo tradeVo = new TradeVo();
        tradeVo.setTotalAmount(totalAmount);
        tradeVo.setOrderItemList(orderItemList);
        tradeVo.setTradeNo(tradeNo);

        return tradeVo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long submitOrder(OrderForm orderForm) {
        //获取用户id
        Long userId = SecurityContextHolder.getUserId();

        //获取去tradeNo
        String tradeNo = orderForm.getTradeNo();

        //1.验证用户是否通过浏览器回退进行重复提交订单
        //1.1 获取Redis中存放流水号 跟用户提交比较
//        OrderInfo orderInfo = new OrderInfo();
//        Boolean flag1 = this.checkTradeNo(orderInfo.getUserId().toString(), tradeNo);
//        if (!flag1) {
//            throw new RuntimeException("请勿重复提交订单，请尝试重试");
//        }
//        //1.2 验证通过，将Redis中存放流水号删除
//        this.deleteTradeNo(orderInfo.getUserId().toString());

        //1.3 采用Lua脚本保证判断删除流水号原子性 KEYS[1]:流水号Key    ARGV[1]：用户流水号
        // 构造用户交易键，用于在Redis中唯一标识用户的交易
        String userTradeKey = "user:tradeNo:" + userId;

        // 定义Lua脚本文本，用于原子操作：检查交易号是否匹配，如果匹配则删除键，否则返回0
        String scriptText = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                "then\n" +
                "    return redis.call(\"del\",KEYS[1])\n" +
                "else\n" +
                "    return 0\n" +
                "end";

        // 创建Redis脚本对象，并设置脚本文本和结果类型
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(scriptText);
        redisScript.setResultType(Long.class);

        // 执行Lua脚本，传递用户交易键和交易号作为参数
        Long flag = (Long) redisTemplate.execute(redisScript, Arrays.asList(userTradeKey), tradeNo);

        // 检查脚本执行结果，如果返回0表示交易号不匹配或键已删除，防止重复提交订单
        if (flag == 0) {
            throw new ServiceException("请勿重复提交订单，请尝试重试");
        }

        //2. 判断购物项
        List<OrderItem> orderItemList = orderForm.getOrderItemList();
        if (CollectionUtils.isEmpty(orderItemList)) {
            throw new ServiceException("请求不合法");
        }

        //3.订单校验
        //3.1.校验价格
        // 提取订单项中的SKU ID，生成SKU ID列表
        List<Long> skuIdList = orderItemList.stream().map(OrderItem::getSkuId).collect(Collectors.toList());

        // 调用远程产品服务，获取SKU价格列表
        R<List<SkuPrice>> skuPriceListResult = remoteProductService.getSkuPriceList(skuIdList, SecurityConstants.INNER);

        // 检查获取SKU价格列表是否失败
        if (R.FAIL == skuPriceListResult.getCode()) {
            // 如果失败，抛出服务异常
            throw new ServiceException(skuPriceListResult.getMsg());
        }

        // 获取SKU价格列表数据
        List<SkuPrice> skuPriceList = skuPriceListResult.getData();

        // 将SKU价格列表转换为SKU ID到销售价格的映射
        Map<Long, BigDecimal> skuIdToSalePriceMap = skuPriceList.stream().collect(Collectors.toMap(SkuPrice::getSkuId, SkuPrice::getSalePrice));

        // 初始化价格检查结果字符串
        String priceCheckResult = "";

        // 遍历订单项，检查价格是否变化
        for (OrderItem orderItem : orderItemList) {
            // 如果价格不匹配，记录到价格检查结果字符串中
            if (orderItem.getSkuPrice().compareTo(skuIdToSalePriceMap.get(orderItem.getSkuId())) != 0) {
                priceCheckResult += orderItem.getSkuName() + "价格变化了; ";
            }
        }

        // 如果价格检查结果字符串不为空
        if(StringUtils.isNotEmpty(priceCheckResult)) {
            // 更新购物车价格
            remoteCartService.updateCartPrice(userId, SecurityConstants.INNER);

            // 抛出服务异常，提示价格变化
            throw new ServiceException(priceCheckResult);
        }

        //3.2.校验库存并锁定库存
        //TODO ...
//        for(OrderItem orderItem : orderItemList) {
//            Long skuId = orderItem.getSkuId();
//            Integer skuNum = orderItem.getSkuNum();
//            R<String> lockMsgR = remoteProductService.checkAndLockStock(tradeNo, skuId, skuNum, SecurityConstants.INNER);
//            String lockMsg = lockMsgR.getData();
//            Assert.isNull(lockMsg, lockMsg);//锁定是否存在
//        }

        List<SkuLockVo> skuLockVos = orderItemList.stream().map(orderItem -> {
            SkuLockVo skuLockVo = new SkuLockVo();
            skuLockVo.setSkuId(orderItem.getSkuId());
            skuLockVo.setSkuNum(orderItem.getSkuNum());
            return skuLockVo;
        }).collect(Collectors.toList());
        R<String> lockMsgR = remoteProductService.checkAndLockStock(tradeNo, skuLockVos, SecurityConstants.INNER);
        String lockMsg = lockMsgR.getData();
        Assert.isTrue(StringUtils.isEmpty(lockMsg),lockMsg);// 锁定是否失败 todo


        Long orderId = null;
        try {
            //4 下单
            orderId = this.saveOrder(orderForm);
        } catch (Exception e) {
            e.printStackTrace();
            //抛出异常
            //4.1 下单失败，解锁库存
            rabbitService.sendMessage(MqConst.EXCHANGE_PRODUCT, MqConst.ROUTING_UNLOCK, orderForm.getTradeNo());
            throw new ServiceException("下单失败");
        }

        //5 删除购物车选项
        remoteCartService.deleteCartCheckedList(userId, SecurityConstants.INNER);
        rabbitService.sendDelayMessage(DelayedMqConfig.exchange_delay, DelayedMqConfig.routing_delay,tradeNo,300);
        return orderId;
    }

    @Override
    public void cancelOrder(String orderNo) {
        // 修改订单状态-已关闭
        QueryWrapper<OrderInfo> orderInfoQueryWrapper = new QueryWrapper<>();
        orderInfoQueryWrapper.eq("order_no",orderNo);
        OrderInfo orderInfo = baseMapper.selectOne(orderInfoQueryWrapper);
        orderInfo.setOrderStatus(-1);
        baseMapper.updateById(orderInfo);
        // 给商品模块发消息，释放库存
        rabbitService.sendMessage(MqConst.EXCHANGE_PRODUCT,MqConst.ROUTING_UNLOCK,orderNo);
    }

    @Override
    public OrderInfo getOrderByOrderNo(String orderNo) {

        QueryWrapper<OrderInfo> orderInfoQueryWrapper = new QueryWrapper<>();
        orderInfoQueryWrapper.eq("order_no",orderNo);
        OrderInfo orderInfo = baseMapper.selectOne(orderInfoQueryWrapper);

        QueryWrapper<OrderItem> orderItemQueryWrapper = new QueryWrapper<>();
        orderItemQueryWrapper.eq("order_id",orderInfo.getId());
        List<OrderItem> orderItemList = orderItemMapper.selectList(orderItemQueryWrapper);
        orderInfo.setOrderItemList(orderItemList);

        return orderInfo;
    }

    @Override
    public int getOrderStatus(String orderNo) {
        QueryWrapper<OrderInfo> orderInfoQueryWrapper = new QueryWrapper<>();
        orderInfoQueryWrapper.eq("order_no",orderNo);
        OrderInfo orderInfo = baseMapper.selectOne(orderInfoQueryWrapper);
        return orderInfo.getOrderStatus();
    }

    @Override
    public OrderInfo getByOrderNo(String orderNo) {
        OrderInfo orderInfo = orderInfoMapper.selectOne(new LambdaQueryWrapper<OrderInfo>().eq(OrderInfo::getOrderNo, orderNo));
        List<OrderItem> orderItemList = orderItemMapper.selectList(new LambdaQueryWrapper<OrderItem>().eq(OrderItem::getOrderId, orderInfo.getId()));
        orderInfo.setOrderItemList(orderItemList);
        return orderInfo;
    }

    @Override
    public void payOrder(String orderNo) {
        QueryWrapper<OrderInfo> orderInfoQueryWrapper = new QueryWrapper<>();
        orderInfoQueryWrapper.eq("order_no",orderNo);
        OrderInfo orderInfo = baseMapper.selectOne(orderInfoQueryWrapper);
        orderInfo.setOrderStatus(1);
        baseMapper.updateById(orderInfo);
    }



    @Transactional(rollbackFor = Exception.class)
    public Long saveOrder(OrderForm orderForm) {
        // 获取当前登录用户的id和用户名，用于后续订单信息的填写
        Long userId = SecurityContextHolder.getUserId();
        String userName = SecurityContextHolder.getUserName();

        // 创建OrderInfo对象，用于存储订单信息
        OrderInfo orderInfo = new OrderInfo();
        // 设置订单号、用户id、昵称和备注信息
        orderInfo.setOrderNo(orderForm.getTradeNo());
        orderInfo.setUserId(userId);
        orderInfo.setNickName(userName);
        orderInfo.setRemark(orderForm.getRemark());

        // 调用远程服务获取用户地址信息
        UserAddress userAddress = remoteUserAddressService.getUserAddress(orderForm.getUserAddressId(), SecurityConstants.INNER).getData();
        // 设置收货人信息
        orderInfo.setReceiverName(userAddress.getName());
        orderInfo.setReceiverPhone(userAddress.getPhone());
        orderInfo.setReceiverTagName(userAddress.getTagName());
        orderInfo.setReceiverProvince(userAddress.getProvinceCode());
        orderInfo.setReceiverCity(userAddress.getCityCode());
        orderInfo.setReceiverDistrict(userAddress.getDistrictCode());
        orderInfo.setReceiverAddress(userAddress.getFullAddress());

        // 遍历订单商品列表，计算总金额
        List<OrderItem> orderItemList = orderForm.getOrderItemList();
        BigDecimal totalAmount = new BigDecimal(0);
        for (OrderItem orderItem : orderItemList) {
            totalAmount = totalAmount.add(orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuNum())));
        }
        // 设置总金额、优惠券金额、原始总金额和运费
        orderInfo.setTotalAmount(totalAmount);
        orderInfo.setCouponAmount(new BigDecimal(0));
        orderInfo.setOriginalTotalAmount(totalAmount);
        orderInfo.setFeightFee(orderForm.getFeightFee());

        // 设置订单状态为待支付，并插入订单信息到数据库
        orderInfo.setOrderStatus(0);
        orderInfoMapper.insert(orderInfo);

        // 保存订单明细
        for (OrderItem orderItem : orderItemList) {
            orderItem.setOrderId(orderInfo.getId());
            orderItemMapper.insert(orderItem);
        }

        // 记录订单日志
        OrderLog orderLog = new OrderLog();
        orderLog.setOrderId(orderInfo.getId());
        orderLog.setProcessStatus(0);
        orderLog.setNote("提交订单");
        orderLogMapper.insert(orderLog);

        // 返回订单id
        return orderInfo.getId();

    }

    /**
     * 渲染订单确认页面-生成用户流水号
     *
     * @param userId
     * @return
     */
    private String generateTradeNo(Long userId) {
        //1.构建流水号Key
        String userTradeKey = "user:tradeNo:" + userId;
        //2.构建流水号value
        String tradeNo = UUID.randomUUID().toString().replaceAll("-", "");
        //3.将流水号存入Redis 暂存5分钟
        redisTemplate.opsForValue().set(userTradeKey, tradeNo, 5, TimeUnit.MINUTES);
        return tradeNo;
    }

    /**
     * 验证页面提交流水号是否有效
     *
     * @param userId
     * @param tradeNo
     * @return
     */
    private Boolean checkTradeNo(String userId, String tradeNo) {
        String userTradeKey = "user:tradeNo:" + userId;
        String redisTradeNo = (String) redisTemplate.opsForValue().get(userTradeKey);
        return tradeNo.equals(redisTradeNo);
    }

    /**
     * 删除流水号
     * @param userId
     */
    private void deleteTradeNo(String userId) {
        String userTradeKey = "user:tradeNo:" + userId;
        redisTemplate.delete(userTradeKey);
    }


}
